#![cfg_attr(not(feature = "std"), no_std)]

/// Edit this file to define custom logic or remove it if it is not needed.
/// Learn more about FRAME and the core library of Substrate FRAME pallets:
/// https://substrate.dev/docs/en/knowledgebase/runtime/frame

// Re-export pallet out
pub use pallet::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;


#[frame_support::pallet]
pub mod pallet {
	use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*};
	use frame_system::pallet_prelude::*;

	#[pallet::config]
	pub trait Config: frame_system::Config {
		/// Because this pallet emits events, it depends on the runtime's definition of an event.
		type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
	}

	#[pallet::pallet]
	#[pallet::generate_store(pub(super) trait Store)]
	pub struct Pallet<T>(_);

	// The pallet's runtime storage items.
	// https://substrate.dev/docs/en/knowledgebase/runtime/storage
	#[pallet::storage]
	#[pallet::getter(fn something)]
	// Learn more about declaring storage items:
	// https://substrate.dev/docs/en/knowledgebase/runtime/storage#declaring-storage-items
	pub type Something<T> = StorageValue<_, u32>;

	// Pallets use events to inform users when important changes are made.
	// https://substrate.dev/docs/en/knowledgebase/runtime/events
	#[pallet::event]
	#[pallet::metadata(T::AccountId = "AccountId")]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		/// Event documentation should end with an array that provides descriptive names for event
		/// parameters. [something, who]
		SomethingStored(u32, AccountId),
	}
}

// Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
	/// Error names should be descriptive.
	NoneValue,
	/// Errors should have helpful documentation associated with them.
	StorageOverflow,
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

// Dispatchable functions allows users to interact with the pallet and invoke state changes.
// These functions materialize as "extrinsics", which are often compared to transactions.
// Dispatchable functions must be annotated with a weight and must return a DispatchResult.
#[pallet::call]
impl<T:Config> Pallet<T> {
	/// An example dispatchable that takes a singles value as a parameter, writes the value to
	/// storage and emits an event. This function must be dispatched by a signed extrinsic.
	#[weight = 10_000 + T::DbWeight::get().writes(1)]
	pub fn do_something(origin: OriginFor<T>, something: u32) -> dispatch::DispatchResultWithPostInfo {
		// Check that the extrinsic was signed and get the signer.
		// This function will return an error if the extrinsic is not signed.
		// https://substrate.dev/docs/en/knowledgebase/runtime/origin
		let who = ensure_signed(origin)?;

		// Update storage.
		Something::put(something);

		// Emit an event.
		Self::deposit_event(Event::SomethingStored(something, who));
		// Return a successful DispatchResult
		Ok(().into())
	}

	/// An example dispatchable that may throw a custom error.
	#[weight = 10_000 + T::DbWeight::get().reads_writes(1,1)]
	pub fn cause_error(origin) -> dispatch::DispatchResultWithPostInfo {
		let _who = ensure_signed(origin)?;

		// Read a value from storage.
		match Something::get() {
			// Return an error if the value has not been set.
			None => Err(Error::<T>::NoneValue)?,
			Some(old) => {
				// Increment the value read from storage; will error in the event of overflow.
				let new = old.checked_add(1).ok_or(Error::<T>::StorageOverflow)?;
				// Update the value in storage with the incremented result.
				Something::put(new);
				Ok(().into())
			},
		}
	}
}
